use crate::MapValueExtractor;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MapValue {
    String(String),
    I16(i16),
    U16(u16),
    I32(i32),
    U32(u32),
    F32(f32),
    I64(i64),
    U64(u64),
    F64(f64),
    Bool(bool),
    OptionValue(Option<Box<MapValue>>),
}
pub trait HashmapCustom {
    fn get_value<T>(&self, key: &str) -> Option<T>
    where
        T: Copy + 'static,
        MapValue: MapValueExtractor<T>;
    fn get_value_ref<T>(&self, key: &str) -> Option<&T>
    where
        MapValue: MapValueExtractor<T>;
    fn set_value<T>(&mut self, key: &str, value: T)
    where
        T: Into<MapValue>;
}
impl HashmapCustom for HashMap<String, MapValue> {
    fn get_value<T>(&self, key: &str) -> Option<T>
    where
        T: Copy + 'static,
        MapValue: MapValueExtractor<T>,
    {
        self.get(key).and_then(MapValue::extract).copied()
    }
    fn get_value_ref<T>(&self, key: &str) -> Option<&T>
    where
        MapValue: MapValueExtractor<T>,
    {
        self.get(key).and_then(MapValue::extract)
    }
    fn set_value<T>(&mut self, key: &str, value: T)
    where
        T: Into<MapValue>,
    {
        self.insert(key.to_string(), value.into());
    }
}
#[cfg(test)]
mod tests {
    use crate::map_struct::HashmapCustom;
    use std::collections::HashMap;

    #[test]
    fn test_set() {
        let mut map = HashMap::new();
        map.set_value("&str", "111" as &str);
        map.set_value("String", "111".to_string());
        map.set_value("i16", 111i16);
        map.set_value("u16", 111u16);
        map.set_value("i32", 111i32);
        map.set_value("u32", 111u32);
        map.set_value("f32", 111.11f32);
        map.set_value("i64", 111i64);
        map.set_value("u64", 111u64);
        map.set_value("f64", 111.11f64);
        assert_eq!(
            Some(&"111".to_string()),
            map.get_value_ref::<String>("&str")
        );
        assert_eq!(None, map.get_value_ref::<String>("&str1"));
        assert_eq!(None, map.get_value_ref::<String>("none"));
        assert_eq!(Some(111i16), map.get_value::<i16>("i16"));
        assert_eq!(Some(111u16), map.get_value::<u16>("u16"));
        assert_eq!(Some(111i32), map.get_value::<i32>("i32"));
        assert_eq!(Some(111u32), map.get_value::<u32>("u32"));
        assert_eq!(Some(111.11f32), map.get_value::<f32>("f32"));
        assert_eq!(Some(111i64), map.get_value::<i64>("i64"));
        assert_eq!(Some(111u64), map.get_value::<u64>("u64"));
        assert_eq!(Some(111.11f64), map.get_value::<f64>("f64"));
    }
    #[test]
    fn test_clone_and_ref() {
        let mut map = HashMap::new();
        map.set_value("i32", 111);
        map.set_value("String", "111".to_string());
        let option = map.get_value::<i32>("i32");
        assert_eq!(Some(111), option);
        let option1 = map.get_value_ref::<String>("String");
        assert_eq!(Some(&"111".to_string()), option1);
    }
}
